/*
*  Copyright (C) 2009  Peter Kist & Jan Ripke
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "GalaxyEngine.h"
#include "Sector.h"
#include "Star.h"

#include <galaxy/AppContext.h>
#include <gfx/Interface.h>
#include <mediator/InterMediator.h>
#include <world/Types.h>
#include <world/PropertyFactory.h>
#include <world/properties/Position.h>

namespace properties = galaxy::world::properties;

// these units are measured in lightyears
#define LOGIC_ENGINE_GALAXY_SIZE 131072
#define LOGIC_ENGINE_SECTOR_SIZE 4
#define LOGIC_ENGINE_GALAXY_VISIBLE_SECTORS 21

#define START_POS 38440.0

namespace galaxy {
    namespace logic {
        namespace engine {

            namespace galaxy {

                Octree<Sector *> GalaxyEngine::m_GalaxyMap(LOGIC_ENGINE_GALAXY_SIZE);

                GalaxyEngine::GalaxyEngine(gamestate::StatePtr& gameState) : Engine (gameState)
                {
                }

                GalaxyEngine::~GalaxyEngine(void)
                {
                }

                bool GalaxyEngine::initialize () {
                    if (initializeEntityProperties () == false) return false;
                    if (initializeGalaxy () == false) return false;
                    //if (displayGalaxy() == false) return false;

                    return true;
                }

                bool GalaxyEngine::terminate () {
                    return true;
                }

                bool GalaxyEngine::activate () {
                    gfx::EntityEvent event (gfx::Entity::GFX_ENTITY_POPULATE);

                    if (getVisibleEntities (event.m_Entities) == false) {
                        return false;
                    }

                    // display entities in gfx module
                    mediator::InterMediator::postEvent(event, mediator::POST_DELAYED);
                    return true;
                }

                bool GalaxyEngine::deactivate () {
                    return true;
                }


                /** Generate a part of the galaxy. The galaxy part that is generated is a SECTOR_SIZE^3 piece
                of the entire galaxy.
                */
                bool GalaxyEngine::initializeGalaxy () {
                    core::Vector<core::Real> start (START_POS, START_POS, START_POS);
                    core::Vector<core::Real> min;

                    for (int x = 0; x < LOGIC_ENGINE_GALAXY_VISIBLE_SECTORS; x += LOGIC_ENGINE_SECTOR_SIZE) {
                        for (int y = 0; y < LOGIC_ENGINE_GALAXY_VISIBLE_SECTORS; y += LOGIC_ENGINE_SECTOR_SIZE) {
                            for (int z = 0; z < LOGIC_ENGINE_GALAXY_VISIBLE_SECTORS; z += LOGIC_ENGINE_SECTOR_SIZE) {
                                min.x = start.x + x;
                                min.y = start.y + y;
                                min.z = start.z + z;

                                Sector * sector = setupSector (min);
                                m_GalaxyMap.set((int)min.x, (int)min.y, (int)min.z, sector);
                            }
                        }
                    }
                    return true;
                }

                /** This method registers the properties that are used by the galaxy engine. The registered
                * properties are used by the entities that are in use by the galaxy engine.
                */
                bool GalaxyEngine::initializeEntityProperties() {
                    properties::Position * position = new properties::Position ();
                    world::PropertyFactory::registerProperty(*position, std::string (PROPERTY_GALAXY_POSITION));

                    return true;
                }


                /** Setup a sector. This will create a sector at the specified (min) position and generates
                * the star(s) that are contained in that sector.
                * @param min The minimal position as a vector
                * @return The (possibly) populated sector or null upon failure
                */
                Sector * GalaxyEngine::setupSector (core::Vector<core::Real>& min) {
                    gamestate::states::GameState * state = getGameState ();

                    Sector * sector = new Sector (state->getUniverse (), min, min + LOGIC_ENGINE_SECTOR_SIZE);
                    //boost::shared_ptr<Sector> sectorPtr = sector->shared_from_this();
                    //world::EntityPtr sectorPtr (sector);
                    sector->setSeed ((int) (min.x * min.y * min.z));

                    Star * star = new Star(sector->shared_from_this());
                    world::PropertyPtr posPtr (star->obtainProperty (std::string (PROPERTY_GALAXY_POSITION)));
                    world::properties::Position * position =
                        (world::properties::Position *) posPtr.get ();
                    position->getPosition() = core::Point<core::Real>(2.0,2.0,2.0);

                    return sector;
                }

                bool GalaxyEngine::getVisibleEntities (std::vector<world::Entity *>& stars) {

                    core::Vector<core::Real> start (START_POS, START_POS, START_POS);
                    core::Vector<core::Real> min;

                    for (int z = 0; z < LOGIC_ENGINE_GALAXY_VISIBLE_SECTORS; z += LOGIC_ENGINE_SECTOR_SIZE) {
                        for (int y = 0; y < LOGIC_ENGINE_GALAXY_VISIBLE_SECTORS; y += LOGIC_ENGINE_SECTOR_SIZE) {
                            for (int x = 0; x < LOGIC_ENGINE_GALAXY_VISIBLE_SECTORS; x += LOGIC_ENGINE_SECTOR_SIZE) {
                                min.x = start.x + x;
                                min.y = start.y + y;
                                min.z = start.z + z;

                                Sector * sector = m_GalaxyMap.at((int)min.x, (int)min.y, (int)min.z);
                                world::Entity::EntitySet owned = sector->getOwnedEntities ();
                                if (! owned.empty ()) {
                                    world::Entity::EntitySet::iterator i = owned.begin ();
                                    while (i != owned.end()) {
                                        stars.push_back ((*i).get());
                                        i++;
                                    }
                                }

                                // FIXME: rewrite this method so that it returns 1 sector only
                                return true;
                            }
                        }
                    }

                    return true;
                }


                gamestate::states::GameState * GalaxyEngine::getGameState() {
                    return (gamestate::states::GameState *) m_State.get ();
                }

            } // namespace galaxy
        } // namespace engine
    } // namespace logic
} // namespace galaxy
